Java 反射

什么是反射

Java反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法,对于任意一个对象,都能过调用他的任意一个方法和属性。这种动态获取的信息以及动态调用对象的方法的功能成为Java语言的反射机制。

反射其实就是把Java类中的各种成分映射成一个个Java的对象。比如一个类包括成员变量,方法,构造方法,接口,包等信息,利用反射技术可以对一个类进行解剖,把各个组成部分映射成一个个对象进行操作。首先需要了解类的加载过程,

类加载过程和对象创建过程

类加载过程

  1. JVM会先去方法区中找有没有相应类的.class存在,如果有直接使用就可以,如果没有,需要把相关类的.class文件加载到方法区。

  2. .class文件加载到方法区时,会分成两部分加载,先加载非静态内容,再加载静态内容

  3. 加载非静态内容:把.class中的所有非静态内容加载到方法区下的非静态区域内

  4. 加载静态内容:

    • 把.class文件中的所有静态内容加载到方法区的静态区域内

    • 静态内容加载完成后,对所有静态变量进行默认初始化

    • 所有的静态变量默认初始化完成之后,再进行显示初始化。

    • 当静态区域下的所有静态变量显示初始化完后,执行静态代码段。

  5. 静态区域下的静态代码快执行完毕之后,整个类的加载就完成了。

对象创建过程

  1. 在堆内存中开辟一块空间

  2. 给开辟的空间分配一个地址

  3. 把对象的所有非静态成员变量加载到所开辟的空间下

  4. 所有的非静态成员加载完成之后,对所有的非静态成员变量进行默认初始化

  5. 默认初始化完成之后,调用构造函数

  6. 构造函数入栈执行时,先执行构造函数中的隐式三步,再执行构造函数。

    • 隐式三步:

      • 执行super语句

      • 对开辟空间下所有非静态成员变量进行显示初始化

      • 执行构造代码块

  7. 构造函数执行完毕并弹栈之后,把空间分配的地址赋值给一个引用对象。

Class类

  1. Class类实现了Serializable, AnnotatedElement, GenericDeclaration, Type这四个接口

  2. final函数头,不可继承

    1
    2
    3
    public final class Class<T>
    extends Object
    implements Serializable, GenericDeclaration, Type, AnnotatedElement
  3. Class类实例表示Java程序中运行的类或者接口,枚举是类的一种而注解是接口的一种。同样数组也是一个可以被反射为Class对象,从而被所有数组来共享其维度和数据。8种基本类型也可以看做为class对象。

  4. 需要注意的是Class类没有公有的构造方法,Class对象是有JVM及ClassLoader记载类的过程中自动创建的。

  5. 方法列表

    • 静态方法
返回值 方法及描述
static Class<?> forName(String className) Returns the Class object associated with the class or interface with the given string name.
static Class<?> forName(String name, boolean initialize, ClassLoader loader) Returns the Class object associated with the class or interface with the given string name, using the given class loader.
  • 常用实例方法
返回值 方法及描述
Class<?>[] getClasses() Returns an array containing Class objects representing all the public classes and interfaces that are members of the class represented by this Class object
ClassLoader getClassLoader() Returns the class loader for the class.
Constructor getConstructor(Class<?>… parameterTypes) Returns a Constructor object that reflects the specified public constructor of the class represented by this Class object.
Field getField(String name) Returns a Field object that reflects the specified public member field of the class or interface represented by this Class object.
Class<?>[] getInterfaces() Determines the interfaces implemented by the class or interface represented by this object.
Method getMethod(String name, Class<?>… parameterTypes) Returns a Method object that reflects the specified public member method of the class or interface represented by this Class object.
T newInstance() Creates a new instance of the class represented by this Class object.

反射的使用

实际上,我们获取Class对象的方法有三种:

  1. Object对象的getClass()方法

  2. 任何数据类型都有一个都有一个“静态”的class属性

  3. 通过Class类的静态方法:forName(String className)

实际中我们需要使用反射来动态的生成对象,而如果已经有了对象也就不需要反射了,多以第一种很少用,第二种需要提前有类的包,同样导入了类的包可以直接创建。因此常用的是第三种,通过一个字符串来将要反射的类的信息传递进来动态生成对象。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
package reflect;
/**
* Created by cdx0312
* 2018/4/20
*/
public class GetClassMethod {
public static void main(String[] args) throws ClassNotFoundException {
//通过getClass方法
Student s1 = new Student();
Class stuClass = s1.getClass();
System.out.println(stuClass.getName());
//通过类来获得Class对象
Class stuClass2 = Student.class;
System.out.println(stuClass2.getName());
// 通过forName来获取
Class stuClass3 = Class.forName("reflect.Student");
System.out.println(stuClass3.getName());
System.out.println(stuClass3 == stuClass2);
}
}

通过反射获取构造方法并使用

Student类:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
package reflect;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
/**
* Created by cdx0312
* 2018/4/20
*/
public class Student {
//(默认的构造方法)
Student(String str){
System.out.println("(默认)的构造方法 s = " + str);
}
//无参构造方法
public Student(){
System.out.println("调用了公有、无参构造方法执行了。。。");
}
//有一个参数的构造方法
public Student(char name){
System.out.println("姓名:" + name);
}
//有多个参数的构造方法
public Student(String name ,int age){
System.out.println("姓名:"+name+"年龄:"+ age);
}
//受保护的构造方法
protected Student(boolean n){
System.out.println("受保护的构造方法 n = " + n);
}
//私有构造方法
private Student(int age){
System.out.println("私有的构造方法 年龄:"+ age);
}
public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
Object obj;
// 1. 加载Class对象
Class<?> aClass = Class.forName("reflect.Student");
// 2. 获取所有的公有构造方法
Constructor<?>[] constructors = aClass.getConstructors();
System.out.println("公有的构造方法:");
for (Constructor constructor : constructors)
System.out.println(constructor);
//3. 获得所有的构造方法
Constructor<?>[] declaredConstructors = aClass.getDeclaredConstructors();
System.out.println("所有声明的构造方法:");
for (Constructor constructor : declaredConstructors)
System.out.println(constructor);
//4. 获得无参构造方法
Constructor constructor1 = aClass.getConstructor(null);
System.out.println("所有公有无参构造方法:");
System.out.println(constructor1);
obj = constructor1.newInstance();
System.out.println("obj = " + obj);
//5. 获得私有的构造方法
Constructor<?> declaredConstructor = aClass.getDeclaredConstructor(int.class);
System.out.println("私有的构造方法:");
System.out.println(declaredConstructor);
declaredConstructor.setAccessible(true);
}
}

运行结果:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
公有的构造方法:
public reflect.Student(java.lang.String,int)
public reflect.Student()
public reflect.Student(char)
所有声明的构造方法:
private reflect.Student(int)
protected reflect.Student(boolean)
public reflect.Student(java.lang.String,int)
reflect.Student(java.lang.String)
public reflect.Student()
public reflect.Student(char)
所有公有无参构造方法:
public reflect.Student()
调用了公有、无参构造方法执行了。。。
obj = reflect.Student@4554617c
私有的构造方法:
private reflect.Student(int)

获得成员变量并调用

  1. 批量的:
    public Method[] getMethods():获取所有”公有方法”;(包含了父类的方法也包含Object类)
    public Method[] getDeclaredMethods():获取所有的成员方法,包括私有的(不包括继承的)
  2. 获取单个的:
    public Method getMethod(String name,Class<?>… parameterTypes),name : 方法名;Class … : 形参的Class类型对象
    public Method getDeclaredMethod(String name,Class<?>… parameterTypes)

  3. 调用方法:
    Method –> public Object invoke(Object obj,Object… args): obj : 要调用方法的对象,args:调用方式时所传递的实参

Student1类:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
package reflect;
/**
* Created by cdx0312
* 2018/4/20
*/
public class Student1 {
public void show1(String s){
System.out.println("调用了:公有的,String参数的show1(): s = " + s);
}
protected void show2(){
System.out.println("调用了:受保护的,无参的show2()");
}
void show3(){
System.out.println("调用了:默认的,无参的show3()");
}
private String show4(int age){
System.out.println("调用了,私有的,并且有返回值的,int参数的show4(): age = " + age);
return "abcd";
}
}

测试类:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
package reflect;
import java.lang.reflect.Method;
/**
* Created by cdx0312
* 2018/4/20
*/
public class MethodInvoke {
public static void main(String[] args) throws Exception{
// 1. 获取Class对象
Class clazz = Class.forName("reflect.Student1");
// 2. 获取所有公有方法并打印
System.out.println("***************获取所有的”公有“方法*******************");
Method[] methods = clazz.getMethods();
for (Method method : methods)
System.out.println(method);
// 3. 获取所有方法并打印
System.out.println("***************获取所有的方法*******************");
methods = clazz.getDeclaredMethods();
for (Method method : methods)
System.out.println(method);
// 4. 获取所有公有show1方法并调用
System.out.println("***************获取show1()方法*******************");
Method show1 = clazz.getMethod("show1", String.class);
System.out.println(show1);
Object o = clazz.getConstructor().newInstance();
show1.invoke(o, "haha");
System.out.println("***************获取私有show4()方法*******************");
Method show4 = clazz.getDeclaredMethod("show4", int.class);
System.out.println(show4);
show4.setAccessible(true);
Object invoke = show4.invoke(o, 188);
System.out.println(invoke);
}
}

输出结果:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
package reflect;
import java.lang.reflect.Method;
/**
* Created by cdx0312
* 2018/4/20
*/
public class MethodInvoke {
public static void main(String[] args) throws Exception{
// 1. 获取Class对象
Class clazz = Class.forName("reflect.Student1");
// 2. 获取所有公有方法并打印
System.out.println("***************获取所有的”公有“方法*******************");
Method[] methods = clazz.getMethods();
for (Method method : methods)
System.out.println(method);
// 3. 获取所有方法并打印
System.out.println("***************获取所有的方法*******************");
methods = clazz.getDeclaredMethods();
for (Method method : methods)
System.out.println(method);
// 4. 获取所有公有show1方法并调用
System.out.println("***************获取show1()方法*******************");
Method show1 = clazz.getMethod("show1", String.class);
System.out.println(show1);
Object o = clazz.getConstructor().newInstance();
show1.invoke(o, "haha");
System.out.println("***************获取私有show4()方法*******************");
Method show4 = clazz.getDeclaredMethod("show4", int.class);
System.out.println(show4);
show4.setAccessible(true);
Object invoke = show4.invoke(o, 188);
System.out.println(invoke);
}
}

反射main方法

Student2类

1
2
3
4
5
6
7
8
9
10
11
package reflect;
/**
* Created by cdx0312
* 2018/4/20
*/
public class Student3 {
public static void main(String[] args) {
System.out.println("Main 方法执行");
}
}

测试类:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
package reflect;
import java.lang.reflect.Method;
/**
* Created by cdx0312
* 2018/4/20
*/
public class MainInvoke {
public static void main(String[] args) throws Exception{
Class clazz = Class.forName("reflect.Student3");
Method main = clazz.getMethod("main", String[].class);
main.invoke(null, (Object) new String[]{"a", "b", "c"});
}
}

其中调用main方法时,第一个参数是对象的类型,由于为静态类,则设置为null就可以,第二个参数是方法参数,注意强制转换。

通过反射运行配置文件的内容

Student4类

1
2
3
4
5
6
7
8
9
10
11
package reflect;
/**
* Created by cdx0312
* 2018/4/20
*/
public class Studnet4 {
public void show() {
System.out.println("show");
}
}

a.txt文件

1
2
className = reflect.Studnet4
methodName = show

测试类:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
package reflect;
import java.io.FileReader;
import java.io.IOException;
import java.lang.reflect.Method;
import java.util.Properties;
/**
* Created by cdx0312
* 2018/4/20
*/
public class TxtInvoke {
public static void main(String[] args) throws Exception{
//通过反射获取Class对象
Class clazz = Class.forName(getValue("className"));
//2获取show()方法
Method method = clazz.getMethod(getValue("methodName"));
//3.调用show()方法
method.invoke(clazz.getDeclaredConstructor().newInstance());
}
//此方法接收一个key,在配置文件中获取相应的value
public static String getValue(String key) throws IOException {
//获取配置文件的对象
Properties properties = new Properties();
//获取输入流
FileReader in = new FileReader("E:\\Java_Code\\JavaSE\\ConsistenceHash\\src\\reflect\\a.txt");
//将流加载到配置文件对象中
properties.load(in);
in.close();
//返回根据key获取的value值
return properties.getProperty(key);
}
}

更新要反射的类只操作配置文件就可以,便于后期维护和更新。

反射越过泛型检查

泛型用于编译器,编译过后进行泛型擦除,可以通过反射来越过泛型检查。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
package reflect;
import java.lang.reflect.Method;
import java.util.ArrayList;
/**
* Created by cdx0312
* 2018/4/20
*/
public class Test {
public static void main(String[] args) throws Exception{
ArrayList<String> strList = new ArrayList<>();
strList.add("aaa");
strList.add("bbb");
// strList.add(100);
//获取ArrayList的Class对象,反向的调用add()方法,添加数据
Class clazz = strList.getClass(); //得到 strList 对象的字节码 对象
//获取add()方法
Method m = clazz.getMethod("add", Object.class);
//调用add()方法
m.invoke(strList, 100);
//遍历集合
for(Object obj : strList){
System.out.println(obj);
}
}
}
Donate comment here